"""
    S3D.Wildcard package, contains Wildcard class that support named wildcards.

    Wildcard examples:
        file.*                      - translated to "^file\..*$"
        (cond1|cond2)file           - translated to "^(cond1|cond2)file$"
        <name>.<ext>                - translated to "^(?P<name>.*)\.(?P<ext>.*)$"
        [opt]name                   - translated to "^(opt)?name$"
"""

from unittest import TestCase
import re

def translate(wildcard):
    # . -> \.
    res = wildcard.replace('.', r'\.')
    # * -> .*
    res = res.replace('*', '.*')
    # <name> -> (?P<name>.*)
    res = re.sub(r"<(?P<name>[^>]*)>", r"(?P<\g<name>>.*)", res)

    # [opt] -> (opt)?
    while True:
        next = re.sub(r"\[(?P<opt>[^\[\]]*)\]", r"(\g<opt>)?", res)
        if next == res:
            break
        res = next

    return "^%s$" % res

class Wildcard:
    __slots__ = ("re")

    def __init__(self, wildcard, ignoreCase = True):
        self.re = re.compile(translate(wildcard), ignoreCase and re.IGNORECASE or 0)

    def match(self, text):
        m = self.re.match(text)
        return m and WildcardMatch(text, m.groupdict())

    def filter(self, data):
        res = []
        for p in data:
            m = self.match(p)
            if m is not None:
                res.append(m)

        return res

class WildcardMatch(dict):
    __slots__ = ("source")

    def __init__(self, source, p_dict):
        super(WildcardMatch, self).__init__(p_dict)
        self.source = source

    def __getattr__(self, name):
        """ Returns dict and WildcardMatch methods, otherwise retrieves data from dictionary. """
        if name in dict.__dict__:
            return dict.__getattr__(self, name)

        if name in WildcardMatch.__dict__ or name in WildcardMatch.__slots__:
            return object.__getattribute__(self, name)

        return name in self and self[name] or ""

    def __repr__(self):
        return "(%s: %s)" % (self.source, dict.__repr__(self))

    def __str__(self):
        return self.source
    
    def rename(self, pattern):
        """ Make name using specified pattern. <name> constructions will be replaced with matched values. """
        def callback(m):
            return getattr(self, m.group("name"))

        return re.sub(r"<(?P<name>[^>]*)>", callback, pattern)
    
class Tests(TestCase):
    def test_translate(self):
        def check(wildcard, translated):
            t = translate(wildcard)
            self.assertEqual(t, translated)

        check("*.cpp",                  "^.*\.cpp$")
        check("ap_*.(c|cpp|h|h*)",      "^ap_.*\.(c|cpp|h|h.*)$")
        check("data_<number>.dat",      "^data_(?P<number>.*)\.dat$")
        check("[prefix]*",              "^(prefix)?.*$")
        check("[pref[ix|a]]-post[a]",   "^(pref(ix|a)?)?-post(a)?$")

    def test_wildcard(self):
        def check(wildcard, match, result = dict()):
            w = Wildcard(wildcard)
            r = w.match(match)
            self.assertEquals(r, result)

        check("*.cpp",              "file.cpp")
        check("n[a]me",             "nme")
        check("n[a]me",             "name")
        check("n[a]me",             "nbme",         None)
        check("<name>.<ext>",       "file.cpp",     dict(name = "file", ext = "cpp"))
        check("<name>_suffix.*",    "file",         None)
        check("<name>_suffix[.*]",  "file_suffix",  dict(name = "file"))
        check("<name>_suffix[.*]",  "file_suffixa", None)
        check("a<name>_suffix[.*]", "file_suffix",  None)
        check("<name>_suffix[.*]",  "file_suffix.", dict(name = "file"))

    def test_wildcardMatch(self):
        w = Wildcard("<name>_num<num>.<ext>")
        r = w.match("somename_num10.cpp")

        self.assertEqual(r.name,    "somename")
        self.assertEqual(r.num,     "10")
        self.assertEqual(r.ext,     "cpp")
        self.assertEqual(r.unknown, "")

        self.assertEqual(r.source,  "somename_num10.cpp")

    def test_wildcardRename(self):
        w = Wildcard("<name>_<suffix>.<ext>")
        m = w.match("a_b_c.cpp")
        self.assertEqual(m, dict(name = "a_b", suffix = "c", ext = "cpp"))
        
        self.assertEqual(m.rename("<name>.<ext>"), "a_b.cpp")
        self.assertEqual(m.rename("<name>__<test>.<ext>"), "a_b__.cpp")

        w = Wildcard("<name>[.<ext>]")
        m = w.match("name")
        self.assertEqual(m, dict(name = "name", ext = None))

        self.assertEqual(m.rename("<name>.<ext>"), "name.")
